<?php

require_once 'Translator.php';

class HtmlBinder
{
    /**
     *
     * @var <type> 
     */
    private static $bindings = array();

    /**
     *
     * @var <type> 
     */
    private static $idPrefix;

    /**
     *
     * @param array $bindings 
     */
    public static function initialize(array $bindings)
    {
        foreach ($bindings as $propertyMetaData)
        {
            $objectHash = spl_object_hash($propertyMetaData->getContainer());
            if (!isset(self::$bindings[$objectHash])) {
                self::$bindings[$objectHash] = array();
            }
            self::$bindings[$objectHash][$propertyMetaData->getPropertyName()] = $propertyMetaData;
        }
    }

    /**
     *
     * @param string $idPrefix
     */
    public static function setIdPrefix($idPrefix)
    {
        self::$idPrefix = $idPrefix;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function checkBoxFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);

        $checked = '';
        if ((bool) $object->$propertyName) {
            $checked = ' checked="checked"';
        }
        echo '<input type="hidden" name="', $propertyMetaData->getName(), '" value="0" />', "\n", '<input type="checkbox" name="', $propertyMetaData->getName(), '" id="', $id, '"', $checked, ' ', $htmlAttributes, "/>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function descriptionFor($object, $propertyName)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        echo $propertyMetaData->getDescription();
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function displayNameFor($object, $propertyName)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        echo $propertyMetaData->getDisplayName();
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function hiddenFor($object, $propertyName, $htmlAttributes = null)
    {
        self::inputFor("hidden", $object, $propertyName, $htmlAttributes);
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $text
     * @param array|string $htmlAttributes
     */
    public static function labelFor($object, $propertyName, $text = null, $htmlAttributes = null)
    {
        $pBC = self::getPropertyMetaDataFor($object, $propertyName);
        if ($text === null) {
            $displayName = $pBC->getDisplayName();

            $text = ($displayName == null) ? ucfirst($propertyName) : $displayName;
        }

        $id = self::getIdFor($pBC);
        echo '<label for="', $id, '"', $htmlAttributes, '>', $text, "</label>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $value
     * @param array|string $htmlAttributes
     */
    public static function radioButtonFor($object, $propertyName, $value, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);

        // TODO: Append escaped $value to $id
        $id = self::getIdFor($propertyMetaData);
        $checked = '';
        if ($object->$propertyName == $value) {
            $checked = ' checked="checked"';
        }
        echo '<input type="radio" name="', $propertyMetaData->getName(), '" value="' , $value, '" id="', $id, '"', $checked, ' ', $htmlAttributes, "/>\n";
    }


    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $value
     * @param array|string $htmlAttributes
     */
    public static function selectFor($object, $propertyName, array $values, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);

        $id = self::getIdFor($propertyMetaData);

        $select = '<select name="' . $propertyMetaData->getName() . '" id="' . $id . '"' . $htmlAttributes . '>';

        $isAssociative = Utility::isArrayAssociative($values);

        foreach ($values as $value => $label) {
            if ($isAssociative) {
                $select .= "\n<option value=\"" . $value . '"';
                if ($object->$propertyName == $value) {
                    $select .= ' selected="selected"';
                }
            } else {
                $select .= "\n<option";
                if ($object->$propertyName == $label) {
                    $select .= ' selected="selected"';
                }
            }
            
            $select .= '>' . $label . '</option>';
        }
        $select .= "\n</select>";
        echo $select;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function textBoxFor($object, $propertyName, $htmlAttributes = null)
    {
        self::inputFor("text", $object, $propertyName, $htmlAttributes);
    }
    
    /**
     *
     * @param string $type
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function inputFor($type, $object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<input type="', $type, '" name="', $propertyMetaData->getName(), '" id="', $id, '" value="', $object->$propertyName, '" ', $htmlAttributes, "/>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function passwordFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<input type="password" name="', $propertyMetaData->getName(), '" id="', $id, '" value="" ', $htmlAttributes, "/>\n";
    }
    
    /**
     *
     * @param object $object
     * @param string $propertyName
     * @return PropertyMetaData
     */
    private static function getPropertyMetaDataFor($object, $propertyName)
    {
        if ($object === null || $propertyName === null)
        {
            throw new InvalidArgumentException('The arguments must not be null.');
        }
        $objectHash = spl_object_hash($object);
        if (!isset(self::$bindings[$objectHash])) {
            throw new Exception("The object supplied is not bound.");
        } else if (!isset(self::$bindings[$objectHash][$propertyName])) {
            throw new Exception("Unknown property '$propertyName'.");
        }
        return self::$bindings[$objectHash][$propertyName];
    }

    /**
     *
     * @param PropertyMetaData $propertyMetaData
     * @return string
     */
    private static function getIdFor(PropertyMetaData $propertyMetaData)
    {
        if (self::$idPrefix == null) {
            return $propertyMetaData->getName();
        }
        return self::$idPrefix . '-' . $propertyMetaData->getName();
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function textareaFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<textarea name="', $propertyMetaData->getName(), '" id="', $id, '" ', $htmlAttributes, ">", $object->$propertyName, "</textarea>";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function validationMessagesFor($object, $propertyName)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $validationResult = $propertyMetaData->getValidationResult();

        if ($validationResult != null && !$validationResult->isValid()) {
            $displayName = $propertyMetaData->getDisplayName();
            $fieldName = ($displayName == null) ? ucfirst($propertyName) : $displayName;
            foreach ($validationResult->getValidationMessages() as $validationMessage) {
                echo '<span>', sprintf(Translator::translate($validationMessage), $fieldName), '</span>';
            }
        }
    }
}